﻿using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Threading;
using System.Net;


public abstract class GenericReader
{
	public GenericReader() { }

	public abstract string ReadString();
	public abstract DateTime ReadDateTime();
	public abstract TimeSpan ReadTimeSpan();
	public abstract DateTime ReadDeltaTime();
	public abstract decimal ReadDecimal();
	public abstract long ReadLong();
	public abstract ulong ReadULong();
	public abstract int ReadInt();
	public abstract uint ReadUInt();
	public abstract short ReadShort();
	public abstract ushort ReadUShort();
	public abstract double ReadDouble();
	public abstract float ReadFloat();
	public abstract char ReadChar();
	public abstract byte ReadByte();
	public abstract sbyte ReadSByte();
	public abstract bool ReadBool();
	public abstract int ReadEncodedInt();
	public abstract IPAddress ReadIPAddress();
	public abstract Point3D ReadPoint3D();
	public abstract Kingdom ReadKingdom();
	public abstract StarSystem ReadStarSystem();
	public abstract Planet ReadPlanet();
	public abstract Fleet ReadFleet();
	public abstract Building ReadBuilding();
	public abstract Race ReadRace();
	public abstract Technology ReadTechnology();

	public abstract bool End();
}

public abstract class GenericWriter
{
	public GenericWriter() { }

	public abstract void Close();

	public abstract long Position { get; }

	public abstract void Write(string value);
	public abstract void Write(DateTime value);
	public abstract void Write(TimeSpan value);
	public abstract void Write(decimal value);
	public abstract void Write(long value);
	public abstract void Write(ulong value);
	public abstract void Write(int value);
	public abstract void Write(uint value);
	public abstract void Write(short value);
	public abstract void Write(ushort value);
	public abstract void Write(double value);
	public abstract void Write(float value);
	public abstract void Write(char value);
	public abstract void Write(byte value);
	public abstract void Write(sbyte value);
	public abstract void Write(bool value);
	public abstract void WriteEncodedInt(int value);
	public abstract void Write(IPAddress value);

	public abstract void WriteDeltaTime(DateTime value);

	public abstract void Write(Point3D value);
	public abstract void Write(Kingdom value);
	public abstract void Write(StarSystem value);
	public abstract void Write(Planet value);
	public abstract void Write(Fleet value);
	public abstract void Write(Building value);
	public abstract void Write(Race value);
	public abstract void Write(Technology value);
}

public class BinaryFileWriter : GenericWriter
{
	private bool PrefixStrings;
	private Stream m_File;

	protected virtual int BufferSize
	{
		get
		{
			return 64 * 1024;
		}
	}

	private byte[] m_Buffer;

	private int m_Index;

	private Encoding m_Encoding;

	public BinaryFileWriter(Stream strm, bool prefixStr)
	{
		PrefixStrings = prefixStr;
		m_Encoding = Utility.UTF8;
		m_Buffer = new byte[BufferSize];
		m_File = strm;
	}

	public BinaryFileWriter(string filename, bool prefixStr)
	{
		PrefixStrings = prefixStr;
		m_Buffer = new byte[BufferSize];
		m_File = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None);
		m_Encoding = Utility.UTF8WithEncoding;
	}

	public void Flush()
	{
		if (m_Index > 0)
		{
			m_Position += m_Index;

			m_File.Write(m_Buffer, 0, m_Index);
			m_Index = 0;
		}
	}

	private long m_Position;

	public override long Position
	{
		get
		{
			return m_Position + m_Index;
		}
	}

	public Stream UnderlyingStream
	{
		get
		{
			if (m_Index > 0)
				Flush();

			return m_File;
		}
	}

	public override void Close()
	{
		if (m_Index > 0)
			Flush();

		m_File.Close();
	}

	public override void WriteEncodedInt(int value)
	{
		uint v = (uint)value;

		while (v >= 0x80)
		{
			if ((m_Index + 1) > m_Buffer.Length)
				Flush();

			m_Buffer[m_Index++] = (byte)(v | 0x80);
			v >>= 7;
		}

		if ((m_Index + 1) > m_Buffer.Length)
			Flush();

		m_Buffer[m_Index++] = (byte)v;
	}

	private byte[] m_CharacterBuffer;
	private int m_MaxBufferChars;
	private const int LargeByteBufferSize = 256;

	internal void InternalWriteString(string value)
	{
		int length = m_Encoding.GetByteCount(value);

		WriteEncodedInt(length);

		if (m_CharacterBuffer == null)
		{
			m_CharacterBuffer = new byte[LargeByteBufferSize];
			m_MaxBufferChars = LargeByteBufferSize / m_Encoding.GetMaxByteCount(1);
		}

		if (length > LargeByteBufferSize)
		{
			int current = 0;
			int charsLeft = value.Length;

			while (charsLeft > 0)
			{
				int charCount = (charsLeft > m_MaxBufferChars) ? m_MaxBufferChars : charsLeft;
				int byteLength = m_Encoding.GetBytes(value, current, charCount, m_CharacterBuffer, 0);

				if ((m_Index + byteLength) > m_Buffer.Length)
					Flush();

				Buffer.BlockCopy(m_CharacterBuffer, 0, m_Buffer, m_Index, byteLength);
				m_Index += byteLength;

				current += charCount;
				charsLeft -= charCount;
			}
		}
		else
		{
			int byteLength = m_Encoding.GetBytes(value, 0, value.Length, m_CharacterBuffer, 0);

			if ((m_Index + byteLength) > m_Buffer.Length)
				Flush();

			Buffer.BlockCopy(m_CharacterBuffer, 0, m_Buffer, m_Index, byteLength);
			m_Index += byteLength;
		}
	}

	public override void Write(string value)
	{
		if (PrefixStrings)
		{
			if (value == null)
			{
				if ((m_Index + 1) > m_Buffer.Length)
					Flush();

				m_Buffer[m_Index++] = 0;
			}
			else
			{
				if ((m_Index + 1) > m_Buffer.Length)
					Flush();

				m_Buffer[m_Index++] = 1;

				InternalWriteString(value);
			}
		}
		else
		{
			InternalWriteString(value);
		}
	}

	public override void Write(DateTime value)
	{
		Write(value.Ticks);
	}

	public override void WriteDeltaTime(DateTime value)
	{
		long ticks = value.Ticks;
		long now = DateTime.Now.Ticks;

		TimeSpan d;

		try { d = new TimeSpan(ticks - now); }
		catch { if (ticks < now) d = TimeSpan.MaxValue; else d = TimeSpan.MaxValue; }

		Write(d);
	}

	public override void Write(IPAddress value)
	{
		Write(Utility.GetLongAddressValue(value));
	}

	public override void Write(TimeSpan value)
	{
		Write(value.Ticks);
	}

	public override void Write(decimal value)
	{
		int[] bits = Decimal.GetBits(value);

		for (int i = 0; i < bits.Length; ++i)
			Write(bits[i]);
	}

	public override void Write(long value)
	{
		if ((m_Index + 8) > m_Buffer.Length)
			Flush();

		m_Buffer[m_Index] = (byte)value;
		m_Buffer[m_Index + 1] = (byte)(value >> 8);
		m_Buffer[m_Index + 2] = (byte)(value >> 16);
		m_Buffer[m_Index + 3] = (byte)(value >> 24);
		m_Buffer[m_Index + 4] = (byte)(value >> 32);
		m_Buffer[m_Index + 5] = (byte)(value >> 40);
		m_Buffer[m_Index + 6] = (byte)(value >> 48);
		m_Buffer[m_Index + 7] = (byte)(value >> 56);
		m_Index += 8;
	}

	public override void Write(ulong value)
	{
		if ((m_Index + 8) > m_Buffer.Length)
			Flush();

		m_Buffer[m_Index] = (byte)value;
		m_Buffer[m_Index + 1] = (byte)(value >> 8);
		m_Buffer[m_Index + 2] = (byte)(value >> 16);
		m_Buffer[m_Index + 3] = (byte)(value >> 24);
		m_Buffer[m_Index + 4] = (byte)(value >> 32);
		m_Buffer[m_Index + 5] = (byte)(value >> 40);
		m_Buffer[m_Index + 6] = (byte)(value >> 48);
		m_Buffer[m_Index + 7] = (byte)(value >> 56);
		m_Index += 8;
	}

	public override void Write(int value)
	{
		if ((m_Index + 4) > m_Buffer.Length)
			Flush();

		m_Buffer[m_Index] = (byte)value;
		m_Buffer[m_Index + 1] = (byte)(value >> 8);
		m_Buffer[m_Index + 2] = (byte)(value >> 16);
		m_Buffer[m_Index + 3] = (byte)(value >> 24);
		m_Index += 4;
	}

	public override void Write(uint value)
	{
		if ((m_Index + 4) > m_Buffer.Length)
			Flush();

		m_Buffer[m_Index] = (byte)value;
		m_Buffer[m_Index + 1] = (byte)(value >> 8);
		m_Buffer[m_Index + 2] = (byte)(value >> 16);
		m_Buffer[m_Index + 3] = (byte)(value >> 24);
		m_Index += 4;
	}

	public override void Write(short value)
	{
		if ((m_Index + 2) > m_Buffer.Length)
			Flush();

		m_Buffer[m_Index] = (byte)value;
		m_Buffer[m_Index + 1] = (byte)(value >> 8);
		m_Index += 2;
	}

	public override void Write(ushort value)
	{
		if ((m_Index + 2) > m_Buffer.Length)
			Flush();

		m_Buffer[m_Index] = (byte)value;
		m_Buffer[m_Index + 1] = (byte)(value >> 8);
		m_Index += 2;
	}

	//public unsafe override void Write(double value)
	//{
	//  if ((m_Index + 8) > m_Buffer.Length)
	//    Flush();

	//  fixed (byte* pBuffer = m_Buffer)
	//    *((double*)(pBuffer + m_Index)) = value;

	//  m_Index += 8;
	//}

	//public unsafe override void Write(float value)
	//{
	//  if ((m_Index + 4) > m_Buffer.Length)
	//    Flush();

	//  fixed (byte* pBuffer = m_Buffer)
	//    *((float*)(pBuffer + m_Index)) = value;

	//  m_Index += 4;
	//}

  public /*unsafe*/ override void Write( double value ) 
  {
      byte[] eightBytes = System.BitConverter.GetBytes( value ); 
      for ( int i = 0; i < 8; i++ ) 
      { 
          Write( eightBytes[i] ); 
      } 

      /* 
      if( (m_Index + 8) > m_Buffer.Length ) 
          Flush(); 

      fixed( byte* pBuffer = m_Buffer ) 
          *((double*)(pBuffer + m_Index)) = value; 

      m_Index += 8; 
       */ 
  }

  public /*unsafe*/ override void Write( float value ) 
  {
      byte[] fourBytes = System.BitConverter.GetBytes( value ); 
      for ( int i = 0; i < 4; i++ ) 
      { 
          Write( fourBytes[i] ); 
      } 

      /* 
      if ( (m_Index + 4) > BufferSize ) 
          Flush(); 

      fixed ( byte* pBuffer = m_Buffer ) 
          *((float*)(&pBuffer[m_Index])) = value; 

      m_Index += 4; 
       */ 
  }

	private char[] m_SingleCharBuffer = new char[1];

	public override void Write(char value)
	{
		if ((m_Index + 8) > m_Buffer.Length)
			Flush();

		m_SingleCharBuffer[0] = value;

		int byteCount = m_Encoding.GetBytes(m_SingleCharBuffer, 0, 1, m_Buffer, m_Index);
		m_Index += byteCount;
	}

	public override void Write(byte value)
	{
		if ((m_Index + 1) > m_Buffer.Length)
			Flush();

		m_Buffer[m_Index++] = value;
	}

	public override void Write(sbyte value)
	{
		if ((m_Index + 1) > m_Buffer.Length)
			Flush();

		m_Buffer[m_Index++] = (byte)value;
	}

	public override void Write(bool value)
	{
		if ((m_Index + 1) > m_Buffer.Length)
			Flush();

		m_Buffer[m_Index++] = (byte)(value ? 1 : 0);
	}

	public override void Write(Point3D value)
	{
		Write(value.m_X);
		Write(value.m_Y);
		Write(value.m_Z);
	}

	public override void Write(Kingdom value)
	{
		if (value != null)
			Write(value.KingdomID);
		else
			Write((int)-1);
	}

	public override void Write(StarSystem value)
	{
		if (value != null)
			Write(value.StarID);
		else
			Write((int)-1);
	}

	public override void Write(Planet value)
	{
		if (value != null)
			Write(value.PlanetID);
		else
			Write((string)"");
	}

	public override void Write(Fleet value)
	{
		if (value != null)
			Write(value.FleetID);
		else
			Write((ulong)0);
	}

	public override void Write(Building value)
	{
		if (value != null)
			Write(value.FacilityID);
		else
			Write((int)-1);
	}

	public override void Write(Race value)
	{
		if (value != null)
			Write((int)value.RaceType);
		else
			Write((int)-1);
	}

	public override void Write(Technology value)
	{
		Write((int)value.Dependent.Length);
		foreach (GameEnums.TechName tn in value.Dependent)
			Write((int)tn);

		Write((int)value.Name);
		Write((int)value.Category);
		Write((int)value.Stage);
		Write(value.Cost);
		Write(value.Key);
		Write(value.Valid);
	}
}

public sealed class BinaryFileReader : GenericReader
{
	private BinaryReader m_File;

	public BinaryFileReader(BinaryReader br) { m_File = br; }

	public void Close()
	{
		m_File.Close();
	}

	public long Position
	{
		get
		{
			return m_File.BaseStream.Position;
		}
	}

	public long Seek(long offset, SeekOrigin origin)
	{
		return m_File.BaseStream.Seek(offset, origin);
	}

	public override string ReadString()
	{
		if (ReadByte() != 0)
			return m_File.ReadString();
		else
			return null;
	}

	public override DateTime ReadDeltaTime()
	{
		long ticks = m_File.ReadInt64();
		long now = DateTime.Now.Ticks;

		if (ticks > 0 && (ticks + now) < 0)
			return DateTime.MaxValue;
		else if (ticks < 0 && (ticks + now) < 0)
			return DateTime.MinValue;

		try { return new DateTime(now + ticks); }
		catch { if (ticks > 0) return DateTime.MaxValue; else return DateTime.MinValue; }
	}

	public override IPAddress ReadIPAddress()
	{
		return new IPAddress(m_File.ReadInt64());
	}

	public override int ReadEncodedInt()
	{
		int v = 0, shift = 0;
		byte b;

		do
		{
			b = m_File.ReadByte();
			v |= (b & 0x7F) << shift;
			shift += 7;
		} while (b >= 0x80);

		return v;
	}

	public override DateTime ReadDateTime()
	{
		return new DateTime(m_File.ReadInt64());
	}

	public override TimeSpan ReadTimeSpan()
	{
		return new TimeSpan(m_File.ReadInt64());
	}

	public override decimal ReadDecimal()
	{
		return m_File.ReadDecimal();
	}

	public override long ReadLong()
	{
		return m_File.ReadInt64();
	}

	public override ulong ReadULong()
	{
		return m_File.ReadUInt64();
	}

	public override int ReadInt()
	{
		return m_File.ReadInt32();
	}

	public override uint ReadUInt()
	{
		return m_File.ReadUInt32();
	}

	public override short ReadShort()
	{
		return m_File.ReadInt16();
	}

	public override ushort ReadUShort()
	{
		return m_File.ReadUInt16();
	}

	public override double ReadDouble()
	{
		return m_File.ReadDouble();
	}

	public override float ReadFloat()
	{
		return m_File.ReadSingle();
	}

	public override char ReadChar()
	{
		return m_File.ReadChar();
	}

	public override byte ReadByte()
	{
		return m_File.ReadByte();
	}

	public override sbyte ReadSByte()
	{
		return m_File.ReadSByte();
	}

	public override bool ReadBool()
	{
		return m_File.ReadBoolean();
	}

	public override Point3D ReadPoint3D()
	{
		return new Point3D(ReadInt(), ReadInt(), ReadInt());
	}

	public override Kingdom ReadKingdom()
	{
		return Core.FindKingdom(ReadInt());
	}

	public override StarSystem ReadStarSystem()
	{
		return Core.FindStarSystem(ReadInt());
	}

	public override Planet ReadPlanet()
	{
		return Core.FindPlanet(ReadString());
	}

	public override Fleet ReadFleet()
	{
		return Core.FindFleet(ReadULong());
	}

	public override Building ReadBuilding()
	{
		return Core.FindBuilding(ReadInt());
	}

	public override Race ReadRace()
	{
		return Core.FindRace(ReadInt());
	}

	public override Technology ReadTechnology()
	{
		int count = ReadInt();
		GameEnums.TechName[] aDepend = new GameEnums.TechName[count];
		if (count > 0)
			for (int i = 0; i < count; i++)
			{
				aDepend[i] = (GameEnums.TechName)ReadInt();
			}

		return new Technology((GameEnums.TechName)ReadInt(), (Technology.TechCategory)ReadInt(), (Galaxy.Civilization)ReadInt(), ReadDouble(), ReadBool(), ReadBool(), aDepend);
	}

	public override bool End()
	{
		return m_File.PeekChar() == -1;
	}

}
